import tkinter as tk
from tkinter import filedialog
from PIL import Image, ImageTk
import numpy as np
from keras.preprocessing.image import img_to_array
from keras.models import load_model
import cv2



class WinGUI(tk.Tk):
    def __init__(self):
        super().__init__()
        self.__win()
        self.tk_text_lv51wypd = self.__tk_text_lv51wypd(self)
        self.tk_button_lv51x1ea = self.__tk_button_lv51x1ea(self)
        self.tk_frame_lv51xfwe = self.__tk_frame_lv51xfwe(self)
        self.tk_canvas_DetectCan = self.__tk_canvas_DetectCan(self.tk_frame_lv51xfwe)
        self.tk_button_OpenCamera = self.__tk_button_OpenCamera(self)
        self.tk_button_lv51zqcd = self.__tk_button_lv51zqcd(self)
        self.tk_canvas_IdentifyImg = self.__tk_canvas_IdentifyImg(self)
        self.tk_label_lv520wei = self.__tk_label_lv520wei(self)
        self.tk_label_lv521c0z = self.__tk_label_lv521c0z(self)
        self.tk_frame_lv521j5d = self.__tk_frame_lv521j5d(self)
        self.tk_canvas_imgUpload = self.__tk_canvas_imgUpload(self.tk_frame_lv521j5d)
        self.tk_text_lv526326 = self.__tk_text_lv526326(self)

    def __win(self):
        self.title("基于CNN的水果识别分类")
        # 设置窗口大小、居中
        width = 800
        height = 600
        screenwidth = self.winfo_screenwidth()
        screenheight = self.winfo_screenheight()
        geometry = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
        self.geometry(geometry)

        self.resizable(width=False, height=False)

    def scrollbar_autohide(self, vbar, hbar, widget):
        """自动隐藏滚动条"""

        def show():
            if vbar:
                vbar.lift(widget)
            if hbar:
                hbar.lift(widget)

        def hide():
            if vbar:
                vbar.lower(widget)
            if hbar:
                hbar.lower(widget)

        hide()
        widget.bind("<Enter>", lambda e: show())
        if vbar:
            vbar.bind("<Enter>", lambda e: show())
        if vbar:
            vbar.bind("<Leave>", lambda e: hide())
        if hbar:
            hbar.bind("<Enter>", lambda e: show())
        if hbar:
            hbar.bind("<Leave>", lambda e: hide())
        widget.bind("<Leave>", lambda e: hide())

    def v_scrollbar(self, vbar, widget, x, y, w, h, pw, ph):
        widget.configure(yscrollcommand=vbar.set)
        vbar.config(command=widget.yview)
        vbar.place(relx=(w + x) / pw, rely=y / ph, relheight=h / ph, anchor='ne')

    def h_scrollbar(self, hbar, widget, x, y, w, h, pw, ph):
        widget.configure(xscrollcommand=hbar.set)
        hbar.config(command=widget.xview)
        hbar.place(relx=x / pw, rely=(y + h) / ph, relwidth=w / pw, anchor='sw')

    def create_bar(self, master, widget, is_vbar, is_hbar, x, y, w, h, pw, ph):
        vbar, hbar = None, None
        if is_vbar:
            vbar = tk.Scrollbar(master)
            self.v_scrollbar(vbar, widget, x, y, w, h, pw, ph)
        if is_hbar:
            hbar = tk.Scrollbar(master, orient="horizontal")
            self.h_scrollbar(hbar, widget, x, y, w, h, pw, ph)
        self.scrollbar_autohide(vbar, hbar, widget)

    # 上传图片下面的框
    def __tk_text_lv51wypd(self, parent):
        text = tk.Text(parent)
        text.place(x=500, y=480, width=246, height=100)
        return text

    # 识别图片上面的框
    def __tk_text_lv526326(self, parent):
        text = tk.Text(parent)
        text.place(x=480, y=70, width=281, height=235)
        return text

    def __tk_button_lv51x1ea(self, parent):
        btn = tk.Button(parent, text="上传图片", takefocus=False)
        btn.place(x=500, y=440, width=240, height=30)
        return btn

    def __tk_button_OpenCamera(self, parent):
        btn = tk.Button(parent, text="打开相机", takefocus=False)
        btn.place(x=500, y=400, width=240, height=30)
        return btn

    def __tk_button_lv51zqcd(self, parent):
        btn = tk.Button(parent, text="拍照", takefocus=False)
        btn.place(x=500, y=360, width=240, height=30)
        return btn

    def __tk_canvas_IdentifyImg(self, parent):
        btn = tk.Button(parent, text="识别图片", takefocus=False)
        btn.place(x=500, y=320, width=240, height=30)
        return btn

    def __tk_frame_lv51xfwe(self, parent):
        frame = tk.Frame(parent)
        frame.place(x=35, y=70, width=350, height=240)
        return frame

    def __tk_canvas_DetectCan(self, parent):
        canvas = tk.Canvas(parent, bg="#aaa")
        canvas.place(x=0, y=0, width=350, height=240)
        return canvas

    def __tk_label_lv520wei(self, parent):
        label = tk.Label(parent, text="上传图像", anchor="center")
        label.place(x=180, y=310, width=50, height=30)
        return label

    def __tk_label_lv521c0z(self, parent):
        label = tk.Label(parent, text="相机", anchor="center")
        label.place(x=180, y=40, width=50, height=30)
        return label

    def __tk_frame_lv521j5d(self, parent):
        frame = tk.Frame(parent)
        frame.place(x=35, y=340, width=350, height=240)
        return frame

    def __tk_canvas_imgUpload(self, parent):
        canvas = tk.Canvas(parent, bg="#aaa")
        canvas.place(x=0, y=0, width=350, height=240)
        return canvas


class Win(WinGUI):
    def __init__(self, controller):
        self.ctl = controller
        super().__init__()
        self.__event_bind()
        self.__style_config()
        self.ctl.init(self)
        self.tk_canvas_DetectCan.config(width=350, height=240)

    def __event_bind(self):
        self.tk_button_lv51x1ea.config(command=self.ctl.upload_image)
        self.tk_button_OpenCamera.config(command=self.ctl.open_camera)
        self.tk_button_lv51zqcd.config(command=self.ctl.screenshot)
        self.tk_canvas_IdentifyImg.config(command=self.ctl.identify_image)

    def __style_config(self):
        pass


class Controller:
    def __init__(self):
        # 加载预训练模型
        self.model = load_model('Incept.h5')
        self.class_labels = ['苹果', '香蕉', '菜花', 'Cherry2', '柠檬', '梨子']
        # 存储已上传的图片对象
        self.uploaded_image = None
        # 用于存储相机对象的全局变量
        self.cap = None
        # 存储识别结果
        self.results = []

    def init(self, win):
        self.win = win

    def display_image(self, image):
        canvas_width = self.win.tk_canvas_imgUpload.winfo_width()
        canvas_height = self.win.tk_canvas_imgUpload.winfo_height()

        # Convert PIL Image to PhotoImage
        photo = ImageTk.PhotoImage(image)

        # Clear previous image on canvas
        self.win.tk_canvas_imgUpload.delete("image")

        # Calculate center coordinates
        x = (canvas_width - photo.width()) // 2
        y = (canvas_height - photo.height()) // 2

        # Create new image on canvas
        self.win.tk_canvas_imgUpload.create_image(x, y, image=photo, anchor='nw', tags="image")

        # Save the reference to the photo to prevent garbage collection
        self.win.tk_canvas_imgUpload.image = photo

    def on_canvas_resize(self, event):
        canvas_width = event.width
        canvas_height = event.height
        self.win.tk_canvas_DetectCan.config(width=canvas_width, height=canvas_height)
        self.win.tk_canvas_DetectCan.itemconfig(self.canvas_image, image=self.photo)

    def upload_image(self):
        file_path = filedialog.askopenfilename()

        if file_path:
            self.uploaded_image = Image.open(file_path)
            self.display_image(self.uploaded_image)
            print(f"Image uploaded: {file_path}")

    def screenshot(self):
        if self.cap is not None and self.cap.isOpened():
            # 读取摄像头帧
            ret, frame = self.cap.read()

            # 如果摄像头成功捕获帧
            if ret:
                # 保存图像
                cv2.imwrite('captured_image.jpg', frame)

                print("Screenshot taken!")

                # 释放先前的相机对象和关闭所有 HighGUI 窗口
                self.cap.release()
                cv2.destroyAllWindows()

                # 重新加载保存的图像到PIL Image对象，并更新self.uploaded_image
                self.uploaded_image = Image.open('captured_image.jpg')

                # 更新画布显示新保存的图像
                self.display_image(self.uploaded_image)

                # 重新启用打开相机按钮
                self.win.tk_button_OpenCamera.config(state='normal')
            else:
                print("No frame captured yet!")
        else:
            print("Camera is not opened!")

    def open_camera(self):
        if self.cap is not None:
            self.cap.release()  # 释放先前的相机对象
            cv2.destroyAllWindows()  # 确保所有 HighGUI 窗口都被关闭

        # 使用 CAP_DSHOW 参数打开相机
        self.cap = cv2.VideoCapture(0)

        def update_frame():
            ret, frame = self.cap.read()

            if ret:
                # 将帧转换为 RGB 格式
                frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

                # 调整帧大小以适应画布
                frame_resized = cv2.resize(frame_rgb, (350, 240))

                # 将帧转换为 ImageTk 格式
                image = Image.fromarray(frame_resized)
                photo = ImageTk.PhotoImage(image)

                # 清除画布上的前一帧图像
                self.win.tk_canvas_DetectCan.delete("image")

                # 计算中心坐标
                x = (350 - photo.width()) // 2
                y = (240 - photo.height()) // 2

                # 在画布上创建新图像
                self.win.tk_canvas_DetectCan.create_image(x, y, image=photo, anchor='nw', tags="image")

                # 保存图片的引用，以防止垃圾回收
                self.win.tk_canvas_DetectCan.image = photo

            # 定时在 10 毫秒后更新下一帧
            self.win.after(200, update_frame)

        # 启动帧更新循环
        update_frame()

        # 禁用打开相机按钮
        self.win.tk_button_OpenCamera.config(state='disabled')

    def identify_image(self):
        if self.uploaded_image:
            # 将PIL图像对象转换为numpy数组，并调整大小为 (224, 224)
            image = self.uploaded_image.resize((224, 224))
            image_array = img_to_array(image) / 255.0
            image_array = np.expand_dims(image_array, axis=0)

            # 使用模型进行预测
            predictions = self.model.predict(image_array)
            predicted_class = np.argmax(predictions[0])
            predicted_label = self.class_labels[predicted_class]

            # 将结果存储到结果列表中
            confidence = predictions[0][predicted_class]
            result_text = f"类别: {predicted_label}, 置信度: {confidence * 100:.2f}%"
            self.results.append(result_text)

            # 在文本框中显示所有识别结果
            self.win.tk_text_lv526326.delete("1.0", tk.END)
            for idx, result in enumerate(self.results, start=1):
                self.win.tk_text_lv526326.insert(tk.END, f"{idx}. {result}\n")

            # 在上传图片下方的文本框中显示“识别成功！”并设置为红色
            self.win.tk_text_lv51wypd.tag_configure("red", foreground="red")
            self.win.tk_text_lv51wypd.delete("1.0", tk.END)
            self.win.tk_text_lv51wypd.insert(tk.END, "识别成功！", "red")

            # 打印预测结果
            print(result_text)
        else:
            print("No image uploaded yet!")


class Win(WinGUI):
    def __init__(self, controller):
        self.ctl = controller
        super().__init__()
        self.__event_bind()
        self.__style_config()
        self.ctl.init(self)
        self.tk_canvas_DetectCan.config(width=350, height=240)

    def __event_bind(self):
        self.tk_button_lv51x1ea.config(command=self.ctl.upload_image)
        self.tk_button_OpenCamera.config(command=self.ctl.open_camera)
        self.tk_button_lv51zqcd.config(command=self.ctl.screenshot)
        self.tk_canvas_IdentifyImg.config(command=self.ctl.identify_image)

    def __style_config(self):
        pass


if __name__ == "__main__":
    ctl = Controller()
    win = Win(ctl)
    win.mainloop()

